# Copyright 2013-present Barefoot Networks, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import time
import sys
import logging
import copy
import pdb

import unittest
import random

import pd_base_tests

from ptf import config
from ptf.testutils import *
from ptf.thriftutils import *

import os
from mirror_test.p4_pd_rpc.ttypes import *
from conn_mgr_pd_rpc.ttypes import *
from mirror_pd_rpc.ttypes import *
from mc_pd_rpc.ttypes import *
from devport_mgr_pd_rpc.ttypes import *
from res_pd_rpc.ttypes import *
from ptf_port import *

try:
    from pltfm_pm_rpc.ttypes import *
except ImportError as e:
    pass


dev_id = 0
if test_param_get("arch") == "tofino":
  MIR_SESS_COUNT = 1024
  MAX_SID_NORM = 1015
  MAX_SID_COAL = 1023
  BASE_SID_NORM = 1
  BASE_SID_COAL = 1016
elif test_param_get("arch") == "tofino2":
  MIR_SESS_COUNT = 256
  MAX_SID_NORM = 255
  MAX_SID_COAL = 255
  BASE_SID_NORM = 0
  BASE_SID_COAL = 0
else:
  assert False, "Unsupported arch %s" % test_param_get("arch")

MCID1=1
MCID2=2
HASH1=1
HASH2=2

def portToBitIdx(port):
    pipe = port_to_pipe(port)
    index = port_to_pipe_local_port(port)
    return 72 * pipe + index

def set_port_map(indicies):
    bit_map = [0] * ((288+7)/8)
    for i in indicies:
        index = portToBitIdx(i)
        bit_map[index/8] = (bit_map[index/8] | (1 << (index%8))) & 0xFF
    return bytes_to_string(bit_map)

def set_lag_map(indicies):
    bit_map = [0] * ((256+7)/8)
    for i in indicies:
        bit_map[i/8] = (bit_map[i/8] | (1 << (i%8))) & 0xFF
    return bytes_to_string(bit_map)

def verify_coal_pkt(self, pkt, port):
    logging.debug("Checking for pkt on port %r", port)
    (_, rcv_port, rcv_pkt, pkt_time) = self.dataplane.poll(port_number=port, timeout=2, exp_pkt=None)
    self.assertTrue(rcv_pkt != None, "Did not receive pkt on %r" % port)
    print hexdump(rcv_pkt)
    sys.stdout.flush()
    #only compare slices
    print hexdump(pkt)
    sys.stdout.flush()

def setup_random(seed_val=0):
    if 0 == seed_val:
        seed_val = int(time.time())
    print
    print "Seed is:", seed_val
    sys.stdout.flush()
    random.seed(seed_val)

def make_port(pipe, local_port):
    assert(pipe >= 0 and pipe < 4)
    assert(local_port >= 0 and local_port < 72)
    return (pipe << 7) | local_port

def port_to_pipe(port):
    local_port = port & 0x7F
    assert(local_port < 72)
    pipe = (port >> 7) & 0x3
    assert(port == ((pipe << 7) | local_port))
    return pipe

def port_to_pipe_local_port(port):
    return port & 0x7F

swports = []
swports_by_pipe = {}
for device, port, ifname in config["interfaces"]:
    if port == 64: continue
    pipe = port_to_pipe(port)
    if pipe not in swports_by_pipe:
        swports_by_pipe[pipe] = []
    if pipe in range(int(test_param_get('num_pipes'))):
        swports.append(port)
        swports.sort()
        swports_by_pipe[pipe].append(port)
        swports_by_pipe[pipe].sort()

if swports == []:
    for pipe in range(int(test_param_get('num_pipes'))):
        for port in range(2):
            swports.append( make_port(pipe,port) )
print "Using ports:", swports
sys.stdout.flush()

def mirror_session(mir_type, mir_dir, sid, egr_port=0, egr_port_v=False,
                   egr_port_queue=0, packet_color=0, mcast_grp_a=0,
                   mcast_grp_a_v=False, mcast_grp_b=0, mcast_grp_b_v=False,
                   max_pkt_len=0, level1_mcast_hash=0, level2_mcast_hash=0,
                   mcast_l1_xid=0, mcast_l2_xid=0, mcast_rid=0, cos=0, c2c=0, extract_len=0, timeout=0,
                   int_hdr=[], hdr_len=0):
  return MirrorSessionInfo_t(mir_type,
                             mir_dir,
                             sid,
                             egr_port,
                             egr_port_v,
                             egr_port_queue,
                             packet_color,
                             mcast_grp_a,
                             mcast_grp_a_v,
                             mcast_grp_b,
                             mcast_grp_b_v,
                             max_pkt_len,
                             level1_mcast_hash,
                             level2_mcast_hash,
                             mcast_l1_xid,
                             mcast_l2_xid,
                             mcast_rid,
                             cos,
                             c2c,
                             extract_len,
                             timeout,
                             int_hdr,
                             hdr_len)

def p0_add(test, shdl, dt, port, egr_port=None, ing_mir=0, egr_mir=0, ing_sid=0, egr_sid=0):
  if egr_port is None: egr_port = port
  mspec = mirror_test_p0_match_spec_t(port)
  aspec = mirror_test_set_md_action_spec_t(egr_port, ing_mir, ing_sid, egr_mir, egr_sid)
  h = test.client.p0_table_add_with_set_md(shdl, dt, mspec, aspec)
  return h

class TestBasicForwarding(pd_base_tests.ThriftInterfaceDataPlane):
  def __init__(self):
    pd_base_tests.ThriftInterfaceDataPlane.__init__(self, ["mirror_test"])

  def runTest(self):
    setup_random()
    dt = DevTarget_t(dev_id, hex_to_i16(0xFFFF))
    shdl = self.conn_mgr.client_init()
    try:
      # Set up the forwarding table to send the packet back out the same port
      # it came in on.
      for port in swports:
        p0_add(self, shdl, dt, port)
      self.conn_mgr.complete_operations(shdl)

      pkt = simple_eth_packet(pktlen=83)
      for port in swports:
        send_packet(self, port, str(pkt))
      for port in swports:
        verify_packet(self, pkt, port)
      verify_no_other_packets(self)

      # Set up the forwarding table to send the packet to an invalid port which
      # drops the packet.
      for port in swports:
        p0_add(self, shdl, dt, port, egr_port=511)
      self.conn_mgr.complete_operations(shdl)

      pkt = simple_eth_packet(pktlen=83)
      for port in swports:
        send_packet(self, port, str(pkt))
      time.sleep(3)
      verify_no_other_packets(self)

    finally:
      entry_count = self.client.p0_get_entry_count(shdl, dt)
      for _ in range(entry_count):
        hdl = self.client.p0_get_first_entry_handle(shdl, dt)
        self.client.p0_table_delete(shdl, dev_id, hdl)
      self.conn_mgr.client_cleanup(shdl)

class TestBasicIngMir(pd_base_tests.ThriftInterfaceDataPlane):
  """
  Check that we can ingress mirror a packet out the port it came in on.
  """
  def __init__(self):
    pd_base_tests.ThriftInterfaceDataPlane.__init__(self, ["mirror_test"])

  def runTest(self):
    setup_random()
    dt = DevTarget_t(dev_id, hex_to_i16(0xFFFF))
    shdl = self.conn_mgr.client_init()
    self.sids = []
    try:
      # Setup a session to mirror the packets out the ingress ports.
      # The original packets will be sent to a dummy port that drops them.
      sids = random.sample(xrange(BASE_SID_NORM, MAX_SID_NORM), len(swports))
      for port,sid in zip(swports, sids):
        p0_add(self, shdl, dt, port, egr_port=511, ing_mir=1, ing_sid=sid)
        info = mirror_session(MirrorType_e.PD_MIRROR_TYPE_NORM,
                              Direction_e.PD_DIR_INGRESS,
                              sid,
                              port,
                              True)
        self.mirror.mirror_session_create(shdl, dt, info)
        self.sids.append(sid)
        print "Using session %d for port %d" % (sid, port)
        sys.stdout.flush()
      self.conn_mgr.complete_operations(shdl)

      pkt = simple_eth_packet(pktlen=79)
      for port in swports:
        send_packet(self, port, str(pkt))
      for port in swports:
        verify_packet(self, pkt, port)
      verify_no_other_packets(self)

      # Disable the sessions and check again
      for sid in self.sids:
        self.mirror.mirror_session_disable(shdl, Direction_e.PD_DIR_INGRESS, dt, sid)
      self.conn_mgr.complete_operations(shdl)

      pkt = simple_eth_packet(pktlen=80)
      for port in swports:
        send_packet(self, port, str(pkt))
      verify_no_other_packets(self)

      # Enable the sessions and check again
      for sid in self.sids:
        self.mirror.mirror_session_enable(shdl, Direction_e.PD_DIR_INGRESS, dt, sid)
      self.conn_mgr.complete_operations(shdl)

      pkt = simple_eth_packet(pktlen=81)
      for port in swports:
        send_packet(self, port, str(pkt))
      for port in swports:
        verify_packet(self, pkt, port)
      verify_no_other_packets(self)

      # Disable the sessions in both directions and check again
      for sid in self.sids:
        self.mirror.mirror_session_disable(shdl, Direction_e.PD_DIR_BOTH, dt, sid)
      self.conn_mgr.complete_operations(shdl)

      pkt = simple_eth_packet(pktlen=82)
      for port in swports:
        send_packet(self, port, str(pkt))
      verify_no_other_packets(self)

      # Enable the sessions and check again
      for sid in self.sids:
        self.mirror.mirror_session_enable(shdl, Direction_e.PD_DIR_INGRESS, dt, sid)
      self.conn_mgr.complete_operations(shdl)

      pkt = simple_eth_packet(pktlen=83)
      for port in swports:
        send_packet(self, port, str(pkt))
      for port in swports:
        verify_packet(self, pkt, port)
      verify_no_other_packets(self)

      # Disable the sessions for other pipes; mirroring should still happen
      for port,sid in zip(swports, sids):
        other_pipes = range(int(test_param_get('num_pipes')))
        other_pipes.remove(port_to_pipe(port))
        for pipe in other_pipes:
          self.mirror.mirror_session_disable(shdl, Direction_e.PD_DIR_INGRESS, DevTarget_t(dev_id, pipe), sid)
      self.conn_mgr.complete_operations(shdl)

      pkt = simple_eth_packet(pktlen=84)
      for port in swports:
        send_packet(self, port, str(pkt))
      for port in swports:
        verify_packet(self, pkt, port)
      verify_no_other_packets(self)

      # Enable in other pipes but disable in the ingress pipe; mirroring will
      # not happen.
      for port,sid in zip(swports, sids):
        other_pipes = range(int(test_param_get('num_pipes')))
        other_pipes.remove(port_to_pipe(port))
        for pipe in other_pipes:
          self.mirror.mirror_session_enable(shdl, Direction_e.PD_DIR_INGRESS, DevTarget_t(dev_id, pipe), sid)
        this_pipe = port_to_pipe(port)
        self.mirror.mirror_session_disable(shdl, Direction_e.PD_DIR_INGRESS, DevTarget_t(dev_id, this_pipe), sid)
      self.conn_mgr.complete_operations(shdl)

      pkt = simple_eth_packet(pktlen=85)
      for port in swports:
        send_packet(self, port, str(pkt))
      verify_no_other_packets(self)

      # Disable the session in all pipes.
      for sid in self.sids:
        self.mirror.mirror_session_disable(shdl, Direction_e.PD_DIR_INGRESS, dt, sid)
      self.conn_mgr.complete_operations(shdl)

      pkt = simple_eth_packet(pktlen=86)
      for port in swports:
        send_packet(self, port, str(pkt))
      verify_no_other_packets(self)

      # Enable the sessions in the local pipe and check again
      for port,sid in zip(swports, sids):
        this_pipe = port_to_pipe(port)
        self.mirror.mirror_session_enable(shdl, Direction_e.PD_DIR_INGRESS, DevTarget_t(dev_id, this_pipe), sid)
      self.conn_mgr.complete_operations(shdl)

      pkt = simple_eth_packet(pktlen=87)
      for port in swports:
        send_packet(self, port, str(pkt))
      for port in swports:
        verify_packet(self, pkt, port)
      verify_no_other_packets(self)

      # Enable the sessions in all pipes and check again
      for sid in self.sids:
        self.mirror.mirror_session_enable(shdl, Direction_e.PD_DIR_INGRESS, dt, sid)
      self.conn_mgr.complete_operations(shdl)

      pkt = simple_eth_packet(pktlen=88)
      for port in swports:
        send_packet(self, port, str(pkt))
      for port in swports:
        verify_packet(self, pkt, port)
      verify_no_other_packets(self)

      # Delete the sessions, add them again in a disabled state.
      while len(self.sids):
        sid = self.sids.pop(0)
        self.mirror.mirror_session_delete(shdl, dt, sid)
      self.conn_mgr.complete_operations(shdl)
      pkt = simple_eth_packet(pktlen=89)
      for port in swports:
        send_packet(self, port, str(pkt))
      verify_no_other_packets(self)
      for port,sid in zip(swports, sids):
        info = mirror_session(MirrorType_e.PD_MIRROR_TYPE_NORM,
                              Direction_e.PD_DIR_NONE,
                              sid,
                              port,
                              True)
        self.mirror.mirror_session_create(shdl, dt, info)
        self.sids.append(sid)
      self.conn_mgr.complete_operations(shdl)
      for port in swports:
        send_packet(self, port, str(pkt))
      verify_no_other_packets(self)

      # Enable the sessions in all pipes and check again
      for sid in self.sids:
        self.mirror.mirror_session_enable(shdl, Direction_e.PD_DIR_INGRESS, dt, sid)
      self.conn_mgr.complete_operations(shdl)

      pkt = simple_eth_packet(pktlen=90)
      for port in swports:
        send_packet(self, port, str(pkt))
      for port in swports:
        verify_packet(self, pkt, port)
      verify_no_other_packets(self)

    finally:
      entry_count = self.client.p0_get_entry_count(shdl, dt)
      for _ in range(entry_count):
        hdl = self.client.p0_get_first_entry_handle(shdl, dt)
        self.client.p0_table_delete(shdl, dev_id, hdl)
      for sid in self.sids:
        self.mirror.mirror_session_delete(shdl, dt, sid)
      self.conn_mgr.client_cleanup(shdl)

class TestBasicEgrMir(pd_base_tests.ThriftInterfaceDataPlane):
  """
  Check that we can ingress mirror a packet out the port it came in on.
  """
  def __init__(self):
    pd_base_tests.ThriftInterfaceDataPlane.__init__(self, ["mirror_test"])

  def runTest(self):
    setup_random()
    dt = DevTarget_t(dev_id, hex_to_i16(0xFFFF))
    shdl = self.conn_mgr.client_init()
    self.sids = []
    try:
      # Setup a session to mirror the packets out the ingress ports.
      # The original packets will be sent back out the ingress ports.
      sids = random.sample(xrange(BASE_SID_NORM, MAX_SID_NORM), len(swports))
      for port,sid in zip(swports, sids):
        p0_add(self, shdl, dt, port, egr_port=port, egr_mir=1, egr_sid=sid)
        info = mirror_session(MirrorType_e.PD_MIRROR_TYPE_NORM,
                              Direction_e.PD_DIR_EGRESS,
                              sid,
                              port,
                              True)
        self.mirror.mirror_session_create(shdl, dt, info)
        self.sids.append(sid)
        print "Using session %d for port %d" % (sid, port)
        sys.stdout.flush()
      self.conn_mgr.complete_operations(shdl)
      #pdb.set_trace()
      pkt = simple_eth_packet(pktlen=79)
      for port in swports:
        send_packet(self, port, str(pkt))
      for port in swports:
        verify_packet(self, pkt, port)
        verify_packet(self, pkt, port)
      verify_no_other_packets(self)

      # Disable the sessions and check again
      for sid in self.sids:
        self.mirror.mirror_session_disable(shdl, Direction_e.PD_DIR_EGRESS, dt, sid)
      self.conn_mgr.complete_operations(shdl)
      pkt = simple_eth_packet(pktlen=80)
      for port in swports:
        send_packet(self, port, str(pkt))
      for port in swports:
        verify_packet(self, pkt, port)
      verify_no_other_packets(self)

      # Enable the sessions and check again
      for sid in self.sids:
        self.mirror.mirror_session_enable(shdl, Direction_e.PD_DIR_EGRESS, dt, sid)
      self.conn_mgr.complete_operations(shdl)

      pkt = simple_eth_packet(pktlen=81)
      for port in swports:
        send_packet(self, port, str(pkt))
      for port in swports:
        verify_packet(self, pkt, port)
        verify_packet(self, pkt, port)
      verify_no_other_packets(self)

      # Disable the sessions in both directions and check again
      for sid in self.sids:
        self.mirror.mirror_session_disable(shdl, Direction_e.PD_DIR_BOTH, dt, sid)
      self.conn_mgr.complete_operations(shdl)

      pkt = simple_eth_packet(pktlen=82)
      for port in swports:
        send_packet(self, port, str(pkt))
      for port in swports:
        verify_packet(self, pkt, port)
      verify_no_other_packets(self)

      # Enable the sessions and check again
      for sid in self.sids:
        self.mirror.mirror_session_enable(shdl, Direction_e.PD_DIR_EGRESS, dt, sid)
      self.conn_mgr.complete_operations(shdl)

      pkt = simple_eth_packet(pktlen=83)
      for port in swports:
        send_packet(self, port, str(pkt))
      for port in swports:
        verify_packet(self, pkt, port)
        verify_packet(self, pkt, port)
      verify_no_other_packets(self)

      # Disable the sessions for other pipes; mirroring should still happen
      for port,sid in zip(swports, sids):
        other_pipes = range(int(test_param_get('num_pipes')))
        other_pipes.remove(port_to_pipe(port))
        for pipe in other_pipes:
          self.mirror.mirror_session_disable(shdl, Direction_e.PD_DIR_EGRESS, DevTarget_t(dev_id, pipe), sid)
      self.conn_mgr.complete_operations(shdl)

      pkt = simple_eth_packet(pktlen=84)
      for port in swports:
        send_packet(self, port, str(pkt))
      for port in swports:
        verify_packet(self, pkt, port)
        verify_packet(self, pkt, port)
      verify_no_other_packets(self)

      # Enable in other pipes but disable in the ingress pipe; mirroring will
      # not happen.
      for port,sid in zip(swports, sids):
        other_pipes = range(int(test_param_get('num_pipes')))
        other_pipes.remove(port_to_pipe(port))
        for pipe in other_pipes:
          self.mirror.mirror_session_enable(shdl, Direction_e.PD_DIR_EGRESS, DevTarget_t(dev_id, pipe), sid)
        this_pipe = port_to_pipe(port)
        self.mirror.mirror_session_disable(shdl, Direction_e.PD_DIR_EGRESS, DevTarget_t(dev_id, this_pipe), sid)
      self.conn_mgr.complete_operations(shdl)

      pkt = simple_eth_packet(pktlen=85)
      for port in swports:
        send_packet(self, port, str(pkt))
      for port in swports:
        verify_packet(self, pkt, port)
      verify_no_other_packets(self)

      # Disable the session in all pipes.
      for sid in self.sids:
        self.mirror.mirror_session_disable(shdl, Direction_e.PD_DIR_EGRESS, dt, sid)
      self.conn_mgr.complete_operations(shdl)

      pkt = simple_eth_packet(pktlen=86)
      for port in swports:
        send_packet(self, port, str(pkt))
      for port in swports:
        verify_packet(self, pkt, port)
      verify_no_other_packets(self)

      # Enable the sessions in the local pipe and check again
      for port,sid in zip(swports, sids):
        this_pipe = port_to_pipe(port)
        self.mirror.mirror_session_enable(shdl, Direction_e.PD_DIR_EGRESS, DevTarget_t(dev_id, this_pipe), sid)
      self.conn_mgr.complete_operations(shdl)

      pkt = simple_eth_packet(pktlen=87)
      for port in swports:
        send_packet(self, port, str(pkt))
      for port in swports:
        verify_packet(self, pkt, port)
        verify_packet(self, pkt, port)
      verify_no_other_packets(self)

      # Enable the sessions in all pipes and check again
      for sid in self.sids:
        self.mirror.mirror_session_enable(shdl, Direction_e.PD_DIR_EGRESS, dt, sid)
      self.conn_mgr.complete_operations(shdl)

      pkt = simple_eth_packet(pktlen=88)
      for port in swports:
        send_packet(self, port, str(pkt))
      for port in swports:
        verify_packet(self, pkt, port)
        verify_packet(self, pkt, port)
      verify_no_other_packets(self)

      # Delete the sessions, add them again in a disabled state.
      while len(self.sids):
        sid = self.sids.pop(0)
        self.mirror.mirror_session_delete(shdl, dt, sid)
      self.conn_mgr.complete_operations(shdl)
      pkt = simple_eth_packet(pktlen=89)
      for port in swports:
        send_packet(self, port, str(pkt))
      for port in swports:
        verify_packet(self, pkt, port)
      verify_no_other_packets(self)
      for port,sid in zip(swports, sids):
        info = mirror_session(MirrorType_e.PD_MIRROR_TYPE_NORM,
                              Direction_e.PD_DIR_NONE,
                              sid,
                              port,
                              True)
        self.mirror.mirror_session_create(shdl, dt, info)
        self.sids.append(sid)
      self.conn_mgr.complete_operations(shdl)
      for port in swports:
        send_packet(self, port, str(pkt))
      for port in swports:
        verify_packet(self, pkt, port)
      verify_no_other_packets(self)

      # Enable the sessions in all pipes and check again
      for sid in self.sids:
        self.mirror.mirror_session_enable(shdl, Direction_e.PD_DIR_EGRESS, dt, sid)
      self.conn_mgr.complete_operations(shdl)

      pkt = simple_eth_packet(pktlen=90)
      for port in swports:
        send_packet(self, port, str(pkt))
      for port in swports:
        verify_packet(self, pkt, port)
        verify_packet(self, pkt, port)
      verify_no_other_packets(self)

    finally:
      entry_count = self.client.p0_get_entry_count(shdl, dt)
      for _ in range(entry_count):
        hdl = self.client.p0_get_first_entry_handle(shdl, dt)
        self.client.p0_table_delete(shdl, dev_id, hdl)
      for sid in self.sids:
        self.mirror.mirror_session_delete(shdl, dt, sid)
      self.conn_mgr.client_cleanup(shdl)

class TestBatching(pd_base_tests.ThriftInterfaceDataPlane):
  """
  Check that we can ingress mirror a packet out the port it came in on.
  """
  def __init__(self):
    pd_base_tests.ThriftInterfaceDataPlane.__init__(self, ["mirror_test"])

  def runTest(self):
    setup_random()
    dt = DevTarget_t(dev_id, hex_to_i16(0xFFFF))
    shdl = self.conn_mgr.client_init()
    shdl2 = self.conn_mgr.client_init()
    self.sids = []
    in_batch = None
    try:
      # Setup a session to mirror the packets out the ingress ports.
      # The original packets will be sent to a dummy port that drops them.
      sids = random.sample(xrange(BASE_SID_NORM, MAX_SID_NORM), len(swports))
      self.conn_mgr.begin_batch(shdl)
      in_batch = shdl
      for port,sid in zip(swports, sids):
        p0_add(self, shdl, dt, port, egr_port=511, ing_mir=1, ing_sid=sid)
        info = mirror_session(MirrorType_e.PD_MIRROR_TYPE_NORM,
                              Direction_e.PD_DIR_INGRESS,
                              sid,
                              port,
                              True)
        self.mirror.mirror_session_create(shdl, dt, info)
        self.sids.append(sid)
        print "Using session %d for port %d" % (sid, port)
        sys.stdout.flush()

      # Before ending the batch ensure another session cannot use the same
      # mirroring session ids.
      for sid in self.sids:
        try:
          self.mirror.mirror_session_disable(shdl2, Direction_e.PD_DIR_INGRESS, dt, sid)
          self.assertTrue(False)
        except InvalidPipeMgrOperation as e:
          pass
      self.conn_mgr.end_batch(shdl, True)
      in_batch = None

      # Verify that the configuration is in place.
      pkt = simple_eth_packet(pktlen=100)
      for port in swports:
        send_packet(self, port, str(pkt))
      for port in swports:
        verify_packet(self, pkt, port)
      verify_no_other_packets(self)

      # Disable the sessions with another session and check again
      for sid in self.sids:
        self.mirror.mirror_session_disable(shdl2, Direction_e.PD_DIR_INGRESS, dt, sid)
      self.conn_mgr.complete_operations(shdl)

      pkt = simple_eth_packet(pktlen=110)
      for port in swports:
        send_packet(self, port, str(pkt))
      verify_no_other_packets(self)

      # Enable the sessions in a batch check again
      self.conn_mgr.begin_batch(shdl2)
      in_batch = shdl2
      for sid in self.sids:
        self.mirror.mirror_session_enable(shdl2, Direction_e.PD_DIR_INGRESS, dt, sid)
        try:
          self.mirror.mirror_session_enable(shdl, Direction_e.PD_DIR_INGRESS, dt, sid)
          self.assertTrue(False)
        except InvalidPipeMgrOperation as e:
          pass
      self.conn_mgr.end_batch(shdl2, True)
      in_batch = None

      pkt = simple_eth_packet(pktlen=120)
      for port in swports:
        send_packet(self, port, str(pkt))
      for port in swports:
        verify_packet(self, pkt, port)
      verify_no_other_packets(self)

      # Disable the sessions in a batch but don't push it, config will not be
      # applied.
      self.conn_mgr.begin_batch(shdl)
      in_batch = shdl
      for sid in self.sids:
        self.mirror.mirror_session_disable(shdl, Direction_e.PD_DIR_INGRESS, dt, sid)
      time.sleep(3)

      pkt = simple_eth_packet(pktlen=130)
      for port in swports:
        send_packet(self, port, str(pkt))
      for port in swports:
        verify_packet(self, pkt, port)
      verify_no_other_packets(self)

      # End the batch and verify that the config is applied.
      self.conn_mgr.end_batch(shdl, True)
      in_batch = None
      pkt = simple_eth_packet(pktlen=140)
      for port in swports:
        send_packet(self, port, str(pkt))
      verify_no_other_packets(self)

    finally:
      if in_batch:
        self.conn_mgr.end_batch(in_batch, False)
      entry_count = self.client.p0_get_entry_count(shdl, dt)
      for _ in range(entry_count):
        hdl = self.client.p0_get_first_entry_handle(shdl, dt)
        self.client.p0_table_delete(shdl, dev_id, hdl)
      for sid in self.sids:
        self.mirror.mirror_session_delete(shdl, dt, sid)
      self.conn_mgr.client_cleanup(shdl)
      self.conn_mgr.client_cleanup(shdl2)

class TestMCast(pd_base_tests.ThriftInterfaceDataPlane):
  """
  Check that we can ingress mirror a packet to a multicast group.
  """
  def __init__(self):
    pd_base_tests.ThriftInterfaceDataPlane.__init__(self, ["mirror_test"])

  def runTest(self):
    setup_random()
    dt = DevTarget_t(dev_id, hex_to_i16(0xFFFF))
    shdl = self.conn_mgr.client_init()
    mc_shdl = self.mc.mc_create_session()
    sids = random.sample(xrange(BASE_SID_NORM,MAX_SID_NORM), 4)
    sid_to_ports = {}
    try:
      # All multicast
      info = mirror_session(MirrorType_e.PD_MIRROR_TYPE_NORM,
                            Direction_e.PD_DIR_INGRESS,
                            sids[0],
                            mcast_grp_a=hex_to_i16(0xAAAA),
                            mcast_grp_a_v=True,
                            mcast_grp_b=hex_to_i16(0x5555),
                            mcast_grp_b_v=True,
                            c2c=1)
      self.mirror.mirror_session_create(shdl, dt, info)
      # Only group 1
      info = mirror_session(MirrorType_e.PD_MIRROR_TYPE_NORM,
                            Direction_e.PD_DIR_INGRESS,
                            sids[1],
                            mcast_grp_a=hex_to_i16(0xAAAA),
                            mcast_grp_a_v=True)
      self.mirror.mirror_session_create(shdl, dt, info)
      # Only group 2
      info = mirror_session(MirrorType_e.PD_MIRROR_TYPE_NORM,
                            Direction_e.PD_DIR_INGRESS,
                            sids[2],
                            mcast_grp_b=hex_to_i16(0x5555),
                            mcast_grp_b_v=True)
      self.mirror.mirror_session_create(shdl, dt, info)
      # Only Copy-to-CPU
      info = mirror_session(MirrorType_e.PD_MIRROR_TYPE_NORM,
                            Direction_e.PD_DIR_INGRESS,
                            sids[3],
                            c2c=1)
      self.mirror.mirror_session_create(shdl, dt, info)

      mgrp_hdl_a = self.mc.mc_mgrp_create(mc_shdl, dev_id, hex_to_i16(0xAAAA))
      mgrp_hdl_b = self.mc.mc_mgrp_create(mc_shdl, dev_id, hex_to_i16(0x5555))
      # Group a will ues a port in each pipe
      ports_a = [swports_by_pipe[p][0] for p in swports_by_pipe]
      # Group b will use a random set of ports
      ports_b = random.sample(swports, 4);
      rid = 1
      node_hdl_a = self.mc.mc_node_create(mc_shdl, dev_id, rid, set_port_map(ports_a), set_lag_map([]))
      node_hdl_b = self.mc.mc_node_create(mc_shdl, dev_id, rid, set_port_map(ports_b), set_lag_map([]))
      self.mc.mc_associate_node(mc_shdl, dev_id, mgrp_hdl_a, node_hdl_a, hex_to_i16(0x8321), 1)
      self.mc.mc_associate_node(mc_shdl, dev_id, mgrp_hdl_b, node_hdl_b, 0, 0)
      self.tm.tm_set_cpuport(dev_id, swports[0])

      self.conn_mgr.complete_operations(shdl)
      self.mc.mc_complete_operations(mc_shdl)

      # Get the pipe vectors for each group
      v1 = self.mc.mc_get_pipe_vec(mc_shdl, dev_id, hex_to_i16(0xAAAA))
      v2 = self.mc.mc_get_pipe_vec(mc_shdl, dev_id, hex_to_i16(0x5555))
      v1_expected = 0
      for p in ports_a:
        v1_expected = v1_expected | (1 << port_to_pipe(p))
      v2_expected = 0
      for p in ports_b:
        v2_expected = v2_expected | (1 << port_to_pipe(p))
      self.assertEqual(v1, v1_expected)
      self.assertEqual(v2, v2_expected)

      # The first session will generate packets for each multicast group and
      # the CPU port
      sid_to_ports[sids[0]] = list(ports_a + ports_b + [swports[0]])
      # The second and third sessions generate copies to one multicast group.
      sid_to_ports[sids[1]] = list(ports_a)
      sid_to_ports[sids[2]] = list(ports_b)
      # The last session generates a copy to the CPU port
      sid_to_ports[sids[3]] = list([swports[0]])

      # Expect all pipe-masks to be the default
      for sid in sids:
        msk = self.mirror.mirror_session_pipe_vec_get(shdl, dt, sid)
        self.assertEqual(msk, 0xF)

      # Send a packet to each session and verify copies are made.
      for sid in sids:
        p0_add(self, shdl, dt, swports[0], egr_port=511, ing_mir=1, ing_sid=sid)
        self.conn_mgr.complete_operations(shdl)
        pkt = simple_eth_packet(pktlen=1000)
        send_packet(self, swports[0], str(pkt))
        for p in sid_to_ports[sid]:
          verify_packet(self, pkt, p)
        verify_no_other_packets(self)

      # Change the pipe mask to one pipe at a time and make sure that only
      # packets for that pipe are sent.
      for pipe in range(int(test_param_get('num_pipes'))):
        msk = 1 << pipe
        for sid in sids:
          self.mirror.mirror_session_pipe_vec_set(shdl, dt, sid, msk)
          m = self.mirror.mirror_session_pipe_vec_get(shdl, dt, sid)
          self.assertEqual(msk, m)

        # Send a packet to each session and verify copies are made only to the
        # configured pipe.
        for sid in sids:
          p0_add(self, shdl, dt, swports[0], egr_port=511, ing_mir=1, ing_sid=sid)
          self.conn_mgr.complete_operations(shdl)
          pkt = simple_eth_packet(pktlen=1000)
          send_packet(self, swports[0], str(pkt))
          for p in sid_to_ports[sid]:
            if pipe == port_to_pipe(p):
              verify_packet(self, pkt, p)
          verify_no_other_packets(self)

      # Change the pipe mask back to all pipes.
      msk = 0
      for pipe in range(int(test_param_get('num_pipes'))):
        msk = msk | (1 << pipe)
      for sid in sids:
        self.mirror.mirror_session_pipe_vec_set(shdl, dt, sid, msk)
        m = self.mirror.mirror_session_pipe_vec_get(shdl, dt, sid)
        self.assertEqual(msk, m)

      # Modify the first session to use the first group.
      self.mirror.mirror_session_disable(shdl, Direction_e.PD_DIR_INGRESS, dt, sids[0])
      info = mirror_session(MirrorType_e.PD_MIRROR_TYPE_NORM,
                            Direction_e.PD_DIR_INGRESS,
                            sids[0],
                            mcast_grp_a=hex_to_i16(0xAAAA),
                            mcast_grp_a_v=True)
      self.mirror.mirror_session_update(shdl, dt, info, True)

      # Send a packet and make sure it comes to all ports in the group.
      p0_add(self, shdl, dt, swports[0], egr_port=511, ing_mir=1, ing_sid=sids[0])
      self.conn_mgr.complete_operations(shdl)
      pkt = simple_eth_packet(pktlen=1000)
      send_packet(self, swports[0], str(pkt))
      for p in ports_a:
        verify_packet(self, pkt, p)
      verify_no_other_packets(self)

      # Modify the session to use an XID that will prune the node, now no
      # copies should be made.
      self.mirror.mirror_session_disable(shdl, Direction_e.PD_DIR_INGRESS, dt, sids[0])
      info = mirror_session(MirrorType_e.PD_MIRROR_TYPE_NORM,
                            Direction_e.PD_DIR_INGRESS,
                            sids[0],
                            mcast_grp_a=hex_to_i16(0xAAAA),
                            mcast_grp_a_v=True,
                            mcast_l1_xid = hex_to_i16(0x8321))
      self.mirror.mirror_session_update(shdl, dt, info, True)
      self.conn_mgr.complete_operations(shdl)
      pkt = simple_eth_packet(pktlen=1000)
      send_packet(self, swports[0], str(pkt))
      time.sleep(1)
      verify_no_other_packets(self)

      # Modify the session to use an XID that will not prune the node, now
      # copies should be made again.
      self.mirror.mirror_session_disable(shdl, Direction_e.PD_DIR_INGRESS, dt, sids[0])
      info = mirror_session(MirrorType_e.PD_MIRROR_TYPE_NORM,
                            Direction_e.PD_DIR_INGRESS,
                            sids[0],
                            mcast_grp_a=hex_to_i16(0xAAAA),
                            mcast_grp_a_v=True,
                            mcast_l1_xid = hex_to_i16(0xFFFF))
      self.mirror.mirror_session_update(shdl, dt, info, True)
      self.conn_mgr.complete_operations(shdl)
      pkt = simple_eth_packet(pktlen=1000)
      send_packet(self, swports[0], str(pkt))
      for p in ports_a:
        verify_packet(self, pkt, p)
      verify_no_other_packets(self)

      # Modify the session to use an RID that will cause L2 pruning.
      self.mirror.mirror_session_disable(shdl, Direction_e.PD_DIR_INGRESS, dt, sids[0])
      info = mirror_session(MirrorType_e.PD_MIRROR_TYPE_NORM,
                            Direction_e.PD_DIR_INGRESS,
                            sids[0],
                            mcast_grp_a=hex_to_i16(0xAAAA),
                            mcast_grp_a_v=True,
                            mcast_l1_xid = hex_to_i16(0xFFFF),
                            mcast_l2_xid = 257,
                            mcast_rid = hex_to_i16(rid))
      self.mc.mc_update_port_prune_table(mc_shdl, dev_id, 257, set_port_map(ports_a[:1]))
      self.mc.mc_complete_operations(mc_shdl)
      self.mirror.mirror_session_update(shdl, dt, info, True)
      self.conn_mgr.complete_operations(shdl)
      pkt = simple_eth_packet(pktlen=1000)
      send_packet(self, swports[0], str(pkt))
      for p in ports_a[1:]:
        verify_packet(self, pkt, p)
      verify_no_other_packets(self)

      # Modify the session with a bad L2 Exclusion Id
      self.mirror.mirror_session_disable(shdl, Direction_e.PD_DIR_INGRESS, dt, sids[0])
      info = mirror_session(MirrorType_e.PD_MIRROR_TYPE_NORM,
                            Direction_e.PD_DIR_INGRESS,
                            sids[0],
                            mcast_grp_a=hex_to_i16(0xAAAA),
                            mcast_grp_a_v=True,
                            mcast_l1_xid = hex_to_i16(0xFFFF),
                            mcast_l2_xid = hex_to_i16(0xFFFF),
                            mcast_rid = hex_to_i16(rid))
      try:
        self.mirror.mirror_session_update(shdl, dt, info, True)
        self.assertTrue(False)
      except InvalidPipeMgrOperation as e:
        pass

      # Modify the session with a bad port
      self.mirror.mirror_session_disable(shdl, Direction_e.PD_DIR_INGRESS, dt, sids[0])
      info = mirror_session(MirrorType_e.PD_MIRROR_TYPE_NORM,
                            Direction_e.PD_DIR_INGRESS,
                            sids[0],
                            egr_port=hex_to_i16(0xFFFF),
                            egr_port_v=True)
      try:
        self.mirror.mirror_session_update(shdl, dt, info, True)
        self.assertTrue(False)
      except InvalidPipeMgrOperation as e:
        pass


    finally:
      while self.mc.mc_mgrp_get_count(mc_shdl, dev_id):
        h = self.mc.mc_mgrp_get_first(mc_shdl, dev_id)
        self.mc.mc_mgrp_destroy(mc_shdl, dev_id, h)
      while self.mc.mc_node_get_count(mc_shdl, dev_id):
        h = self.mc.mc_node_get_first(mc_shdl, dev_id)
        self.mc.mc_node_destroy(mc_shdl, dev_id, h)
      for sid in sids:
        self.mirror.mirror_session_delete(shdl, dt, sid)
      self.mc.mc_update_port_prune_table(mc_shdl, dev_id, 257, set_port_map([]))
      self.conn_mgr.client_cleanup(shdl)
      self.mc.mc_destroy_session(mc_shdl)

class TestIngMirMulticast(pd_base_tests.ThriftInterfaceDataPlane):
  """
  Check that we can change hash value to ingress mirror a packet out a different multicast port by lag/ecmp.
  """
  def __init__(self):
    pd_base_tests.ThriftInterfaceDataPlane.__init__(self, ["mirror_test"])

  def runTest(self):
    setup_random()
    dt = DevTarget_t(dev_id, hex_to_i16(0xFFFF))
    shdl = self.conn_mgr.client_init()
    mc_sess_hdl = self.mc.mc_create_session()
    sids = random.sample(xrange(BASE_SID_NORM, MAX_SID_NORM), len(swports))
    self.sids = []
    try:
      #configure mc_tbl of mcid with two mc_nodes with 1/2 hash value
      mgrp_hdl = self.mc.mc_mgrp_create(mc_sess_hdl, dev_id, hex_to_i16(MCID1))
      members = [swports[2], swports[3]]
      port_map = set_port_map(members)
      self.mc.mc_set_lag_membership(mc_sess_hdl, dev_id, 1, port_map)
      lag_map = set_lag_map([1])
      port_map = set_port_map([])
      l1_hdl = self.mc.mc_node_create(mc_sess_hdl, dev_id, 1, port_map, lag_map)
      self.mc.mc_associate_node(mc_sess_hdl, dev_id, mgrp_hdl, l1_hdl, 0, 0)
      self.mc.mc_complete_operations(mc_sess_hdl)
      #configure mirror session with multicast, mcid and hash value from session table(default)
      for port,sid in zip(swports[0:1], sids[0:1]):
        p0_add(self, shdl, dt, port, egr_port=511, ing_mir=1, ing_sid=sid)
        info = mirror_session(MirrorType_e.PD_MIRROR_TYPE_NORM,
                            Direction_e.PD_DIR_INGRESS,
                            sid,
                            mcast_grp_a=MCID1,
                            mcast_grp_a_v=True,
                            level2_mcast_hash=HASH1)
        self.mirror.mirror_session_create(shdl, dt, info)
        self.sids.append(sid)
        print "Using session %d for port %d" % (sid, port)
        sys.stdout.flush()
      self.conn_mgr.complete_operations(shdl)
      #pdb.set_trace()

      pkt = simple_eth_packet(pktlen=80)
      for port in swports[0:1]:
        send_packet(self, port, str(pkt))
        time.sleep(5)
        verify_packet(self, pkt, swports[3])
        #verify_packet(self, pkt, swports[1])
        verify_no_other_packets(self)

      if test_param_get("arch") != "tofino":
        for sid in self.sids:
          self.mirror.mirror_session_disable(shdl, Direction_e.PD_DIR_INGRESS, dt, sid)
          #configure hash from p4, which set to HASH2;
          self.mirror.mirror_session_meta_flag_update(shdl, dt, sid, MetaFlag_e.PD_HASH_CFG, 1)
          self.mirror.mirror_session_enable(shdl, Direction_e.PD_DIR_INGRESS, dt, sid)
        #pdb.set_trace()
        self.conn_mgr.complete_operations(shdl)
        pkt = simple_eth_packet(pktlen=80)
        for port in swports[0:1]:
          send_packet(self, port, str(pkt))
          time.sleep(5)
          verify_packet(self, pkt, swports[2])
          #verify_packet(self, pkt, swports[1])
          verify_no_other_packets(self)
        for sid in self.sids:
          self.mirror.mirror_session_disable(shdl, Direction_e.PD_DIR_INGRESS, dt, sid)
          #configure mc_cfg_f to 1, use mc_ctrl to control multicast work or not.
          self.mirror.mirror_session_meta_flag_update(shdl, dt, sid, MetaFlag_e.PD_MC_CFG, 1)
          self.mirror.mirror_session_enable(shdl, Direction_e.PD_DIR_INGRESS, dt, sid)
        #pdb.set_trace()
        self.conn_mgr.complete_operations(shdl)
        pkt = simple_eth_packet(pktlen=80)
        for port in swports[0:1]:
          send_packet(self, port, str(pkt))
          time.sleep(5)
          #verify_packet(self, pkt, swports[1])
          verify_no_other_packets(self)
        for sid in self.sids:
          self.mirror.mirror_session_disable(shdl, Direction_e.PD_DIR_INGRESS, dt, sid)
        #pdb.set_trace()
        self.conn_mgr.complete_operations(shdl)

    finally:
      while self.mc.mc_mgrp_get_count(mc_sess_hdl, dev_id):
        h = self.mc.mc_mgrp_get_first(mc_sess_hdl, dev_id)
        self.mc.mc_mgrp_destroy(mc_sess_hdl, dev_id, h)
      while self.mc.mc_node_get_count(mc_sess_hdl, dev_id):
        h = self.mc.mc_node_get_first(mc_sess_hdl, dev_id)
        self.mc.mc_node_destroy(mc_sess_hdl, dev_id, h)
      self.mc.mc_destroy_session(mc_sess_hdl)
      entry_count = self.client.p0_get_entry_count(shdl, dt)
      for _ in range(entry_count):
        hdl = self.client.p0_get_first_entry_handle(shdl, dt)
        self.client.p0_table_delete(shdl, dev_id, hdl)

      for sid in self.sids:
        self.mirror.mirror_session_delete(shdl, dt, sid)
      self.conn_mgr.client_cleanup(shdl)


class TestIngCoalMir(pd_base_tests.ThriftInterfaceDataPlane):
  """
  Check coal pkt
  """
  def __init__(self):
    pd_base_tests.ThriftInterfaceDataPlane.__init__(self, ["mirror_test"])

  def runTest(self):
    setup_random()
    if test_param_get("arch") == "tofino":
      return
    dt = DevTarget_t(dev_id, hex_to_i16(0xFFFF))
    shdl = self.conn_mgr.client_init()
    self.sids = []
    try:
      sids = random.sample(xrange(BASE_SID_COAL, MAX_SID_COAL), len(swports))
      for port,sid in zip(swports[0:1], sids[0:1]):
        p0_add(self, shdl, dt, port, egr_port=511, ing_mir=1, ing_sid=sid)
        info = mirror_session(MirrorType_e.PD_MIRROR_TYPE_COAL,
                              Direction_e.PD_DIR_INGRESS,
                              sid,
                              port+1,
                              True,
                              extract_len = 16,
                              int_hdr=[0x99, 0x22222222, 0x0, 0x88],
                              hdr_len=12)
        self.mirror.mirror_session_create(shdl, dt, info)
        self.sids.append(sid)
        print "Using session %d for port %d" % (sid, port)
        sys.stdout.flush()
      self.conn_mgr.complete_operations(shdl)

      pkt = simple_eth_packet(pktlen=80, eth_dst='11:22:33:44:55:66', eth_src='99:AA:BB:CC:DD:EE')
      #pdb.set_trace()
      for port in swports[0:1]:
        print"Sending pkts from port %d to test tofino2 mode coal, extract_len=16,hdr_len=12" % port
        sys.stdout.flush()
        send_packet(self, port, str(pkt))
        send_packet(self, port, str(pkt))
        #pdb.set_trace()
        time.sleep(5)
        verify_coal_pkt(self, pkt, port+1)
        verify_no_other_packets(self)
        #pdb.set_trace()
      for sid in self.sids:
        self.mirror.mirror_session_disable(shdl, Direction_e.PD_DIR_INGRESS, dt, sid)
        self.conn_mgr.complete_operations(shdl)
        self.mirror.mirror_session_coal_mode_update(shdl, dt, sid, 1)
        self.conn_mgr.complete_operations(shdl)
        self.mirror.mirror_session_enable(shdl, Direction_e.PD_DIR_INGRESS, dt, sid)
        self.conn_mgr.complete_operations(shdl)
      for port in swports[0:1]:
        print"Sending pkts from port %d to test tof mode of tofino2 coal mirror" % port
        sys.stdout.flush()
        send_packet(self, port, str(pkt))
        send_packet(self, port, str(pkt))
        time.sleep(5)
        verify_coal_pkt(self, pkt, port+1)
        verify_no_other_packets(self)
      for sid in self.sids:
        self.mirror.mirror_session_disable(shdl, Direction_e.PD_DIR_INGRESS, dt, sid)
        self.conn_mgr.complete_operations(shdl)
        info = mirror_session(MirrorType_e.PD_MIRROR_TYPE_COAL,
                              Direction_e.PD_DIR_INGRESS,
                              sid,
                              port+1,
                              True,
                              extract_len = 20,
                              int_hdr=[0x99, 0x22222222, 0x0, 0xef],
                              hdr_len=16)
        self.mirror.mirror_session_update(shdl, dt, info, True)
        self.conn_mgr.complete_operations(shdl)
      for port in swports[0:1]:
        print"Sending pkts from port %d to test extract_len=20,hdr_len=16" % port
        sys.stdout.flush()
        send_packet(self, port, str(pkt))
        send_packet(self, port, str(pkt))
        time.sleep(5)
        verify_coal_pkt(self, pkt, port+1)
        verify_no_other_packets(self)
    finally:
      entry_count = self.client.p0_get_entry_count(shdl, dt)
      for _ in range(entry_count):
        hdl = self.client.p0_get_first_entry_handle(shdl, dt)
        self.client.p0_table_delete(shdl, dev_id, hdl)
      for sid in self.sids:
        self.mirror.mirror_session_delete(shdl, dt, sid)
      self.conn_mgr.client_cleanup(shdl)

class TestEgrCoalMir(pd_base_tests.ThriftInterfaceDataPlane):
  """
  Check coal pkt
  """
  def __init__(self):
    pd_base_tests.ThriftInterfaceDataPlane.__init__(self, ["mirror_test"])

  def runTest(self):
    setup_random()
    if test_param_get("arch") == "tofino":
      return
    dt = DevTarget_t(dev_id, hex_to_i16(0xFFFF))
    shdl = self.conn_mgr.client_init()
    self.sids = []
    try:
      sids = random.sample(xrange(BASE_SID_COAL, MAX_SID_COAL), len(swports))
      for port,sid in zip(swports[0:1], sids[0:1]):
        p0_add(self, shdl, dt, port, egr_port=(port+2), egr_mir=1, egr_sid=sid)
        info = mirror_session(MirrorType_e.PD_MIRROR_TYPE_COAL,
                              Direction_e.PD_DIR_EGRESS,
                              sid,
                              port+1,
                              True,
                              extract_len = 32,
                              int_hdr=[0x99, 0x0, 0x0, 0xee],
                              hdr_len=8)
        self.mirror.mirror_session_create(shdl, dt, info)
        self.sids.append(sid)
        print "Using session %d for port %d" % (sid, port)
        sys.stdout.flush()
      self.conn_mgr.complete_operations(shdl)

      pkt = simple_eth_packet(pktlen=80, eth_dst='11:22:33:44:55:66', eth_src='99:AA:BB:CC:DD:EE')
      #pdb.set_trace()
      for port in swports[0:1]:
        print"Sending pkts from port %d to test tofino2 mode Egress coal, extract_len=32,hdr_len=8" % port
        sys.stdout.flush()
        send_packet(self, port, str(pkt))
        send_packet(self, port, str(pkt))
        #pdb.set_trace()
        time.sleep(3)
        verify_coal_pkt(self, pkt, port+1)
    finally:
      entry_count = self.client.p0_get_entry_count(shdl, dt)
      for _ in range(entry_count):
        hdl = self.client.p0_get_first_entry_handle(shdl, dt)
        self.client.p0_table_delete(shdl, dev_id, hdl)
      for sid in self.sids:
        self.mirror.mirror_session_delete(shdl, dt, sid)
      self.conn_mgr.client_cleanup(shdl)

class TestMirInfoGet(pd_base_tests.ThriftInterfaceDataPlane):
  """
  Check Mirror info
  """
  def __init__(self):
    pd_base_tests.ThriftInterfaceDataPlane.__init__(self, ["mirror_test"])

  def runTest(self):
    if test_param_get("arch") == "tofino":
      return
    setup_random()
    dt = DevTarget_t(dev_id, hex_to_i16(0xFFFF))
    shdl = self.conn_mgr.client_init()
    self.sids = []
    try:
      sids = random.sample(xrange(BASE_SID_NORM, MAX_SID_NORM), len(swports))
      for port,sid in zip(swports, sids):
        p0_add(self, shdl, dt, port, egr_port=511, ing_mir=1, ing_sid=sid)
        info = mirror_session(MirrorType_e.PD_MIRROR_TYPE_NORM,
                              Direction_e.PD_DIR_INGRESS,
                              sid,
                              port,
                              True)
        self.mirror.mirror_session_create(shdl, dt, info)
        self.sids.append(sid)
        print "Using session %d for port %d" % (sid, port)
        sys.stdout.flush()
      self.conn_mgr.complete_operations(shdl)
      print "Test session_info get first"
      sys.stdout.flush()
      next_s_info = self.mirror.mirror_session_get_first(shdl, dev_id)
      #pdb.set_trace()
      self.assertTrue(next_s_info.mir_id == self.sids[0], "Get first Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
      self.assertTrue(next_s_info.mir_type == MirrorType_e.PD_MIRROR_TYPE_NORM, "Get first Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
      self.assertTrue(next_s_info.direction == Direction_e.PD_DIR_INGRESS, "Get first Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
      self.assertTrue(next_s_info.egr_port == swports[0], "Get first Mirror Session Info error: sid %d, port %d, expected port value %d" % (next_s_info.mir_id, next_s_info.egr_port, swports[0]))
      self.assertTrue(next_s_info.egr_port_v == True, "Get first Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
      print "Test session_info get next"
      sys.stdout.flush()
      i = 1
      for sid in self.sids[1:]:
        next_s_info = self.mirror.mirror_session_get_next(shdl, dev_id, next_s_info.mir_id)
        self.assertTrue(next_s_info.mir_id == sid, "Get next Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
        self.assertTrue(next_s_info.mir_type == MirrorType_e.PD_MIRROR_TYPE_NORM, "Get next Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
        self.assertTrue(next_s_info.direction == Direction_e.PD_DIR_INGRESS, "Get next Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
        self.assertTrue(next_s_info.egr_port == swports[i], "Get next Mirror Session Info error: sid %d, port %d, expected port value %d" % (next_s_info.mir_id, next_s_info.egr_port, swports[i]))
        self.assertTrue(next_s_info.egr_port_v == True, "Get next Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
        i = i + 1

      for sid in self.sids:
        self.mirror.mirror_session_delete(shdl, dt, sid)
      #pdb.set_trace()
      print "Test max session_id get"
      sys.stdout.flush()
      max_sid = self.mirror.mirror_session_get_max_session_id(shdl, dev_id, MirrorType_e.PD_MIRROR_TYPE_NORM)
      info = mirror_session(MirrorType_e.PD_MIRROR_TYPE_NORM,
                              Direction_e.PD_DIR_INGRESS,
                              max_sid,
                              swports[0],
                              True)
      self.mirror.mirror_session_create(shdl, dt, info)
      self.conn_mgr.complete_operations(shdl)
      next_s_info = self.mirror.mirror_session_get_first(shdl, dev_id)
      self.assertTrue(next_s_info.mir_id == MAX_SID_NORM, "Get first Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
      self.assertTrue(next_s_info.mir_type == MirrorType_e.PD_MIRROR_TYPE_NORM, "Get first Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
      self.assertTrue(next_s_info.direction == Direction_e.PD_DIR_INGRESS, "Get first Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
      self.assertTrue(next_s_info.egr_port == swports[0], "Get first Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
      self.assertTrue(next_s_info.egr_port_v == True, "Get first Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
      self.mirror.mirror_session_delete(shdl, dt, max_sid)
      #pdb.set_trace()
      if test_param_get("arch") != "tofino":
        max_sid = self.mirror.mirror_session_get_max_session_id(shdl, dev_id, MirrorType_e.PD_MIRROR_TYPE_COAL)
        info = mirror_session(MirrorType_e.PD_MIRROR_TYPE_COAL,
                              Direction_e.PD_DIR_EGRESS,
                              max_sid,
                              swports[0],
                              True)
        self.mirror.mirror_session_create(shdl, dt, info)
        self.conn_mgr.complete_operations(shdl)
        next_s_info = self.mirror.mirror_session_get_first(shdl, dev_id)
        self.assertTrue(next_s_info.mir_id == MAX_SID_COAL, "Get first Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
        self.assertTrue(next_s_info.mir_type == MirrorType_e.PD_MIRROR_TYPE_COAL, "Get first Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
        self.assertTrue(next_s_info.direction == Direction_e.PD_DIR_EGRESS, "Get first Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
        self.assertTrue(next_s_info.egr_port == swports[0], "Get first Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
        self.assertTrue(next_s_info.egr_port_v == True, "Get first Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
        self.mirror.mirror_session_delete(shdl, dt, max_sid)

      print "Test base session_id get"
      sys.stdout.flush()
      base_sid = self.mirror.mirror_session_get_base_session_id(shdl, dev_id, MirrorType_e.PD_MIRROR_TYPE_NORM)
      info = mirror_session(MirrorType_e.PD_MIRROR_TYPE_NORM,
                              Direction_e.PD_DIR_INGRESS,
                              base_sid,
                              swports[0],
                              True)
      self.mirror.mirror_session_create(shdl, dt, info)
      self.conn_mgr.complete_operations(shdl)
      next_s_info = self.mirror.mirror_session_get_first(shdl, dev_id)
      self.assertTrue(next_s_info.mir_id == BASE_SID_NORM, "Get first Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
      self.assertTrue(next_s_info.mir_type == MirrorType_e.PD_MIRROR_TYPE_NORM, "Get first Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
      self.assertTrue(next_s_info.direction == Direction_e.PD_DIR_INGRESS, "Get first Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
      self.assertTrue(next_s_info.egr_port == swports[0], "Get first Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
      self.assertTrue(next_s_info.egr_port_v == True, "Get first Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
      self.mirror.mirror_session_delete(shdl, dt, base_sid)

      if test_param_get("arch") != "tofino":
        base_sid = self.mirror.mirror_session_get_base_session_id(shdl, dev_id, MirrorType_e.PD_MIRROR_TYPE_COAL)
        info = mirror_session(MirrorType_e.PD_MIRROR_TYPE_COAL,
                              Direction_e.PD_DIR_INGRESS,
                              base_sid,
                              swports[0],
                              True)
        self.mirror.mirror_session_create(shdl, dt, info)
        self.conn_mgr.complete_operations(shdl)
        next_s_info = self.mirror.mirror_session_get_first(shdl, dev_id)
        self.assertTrue(next_s_info.mir_id == BASE_SID_COAL, "Get first Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
        self.assertTrue(next_s_info.mir_type == MirrorType_e.PD_MIRROR_TYPE_COAL, "Get first Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
        self.assertTrue(next_s_info.direction == Direction_e.PD_DIR_INGRESS, "Get first Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
        self.assertTrue(next_s_info.egr_port == swports[0], "Get first Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
        self.assertTrue(next_s_info.egr_port_v == True, "Get first Mirror Session Info error: sid %d, mir_type %d, dir %d" % (next_s_info.mir_id, next_s_info.mir_type, next_s_info.direction))
        self.mirror.mirror_session_delete(shdl, dt, base_sid)

      print "Special case tests"
      sys.stdout.flush()
      try:
        next_s_info = self.mirror.mirror_session_get_first(shdl, dev_id)
        self.assertTrue(False)
      except InvalidPipeMgrOperation as e:
        pass

      if test_param_get("arch") != "tofino":
        sids_coal = random.sample(xrange(BASE_SID_COAL, MAX_SID_COAL), len(swports))
        info = mirror_session(MirrorType_e.PD_MIRROR_TYPE_COAL,
                              Direction_e.PD_DIR_EGRESS,
                              sids_coal[0],
                              swports[0],
                              True)
        self.mirror.mirror_session_create(shdl, dt, info)
        self.conn_mgr.complete_operations(shdl)
        first_s_info = self.mirror.mirror_session_get_first(shdl, dev_id)
        self.assertTrue(first_s_info.mir_id == sids_coal[0], "Get first Mirror Session Info error: sid %d, expected sid %d" % (first_s_info.mir_id, sids_coal[0]))
        try:
          next_s_info = self.mirror.mirror_session_get_next(shdl, dev_id, first_s_info.mir_id)
          self.assertTrue(False)
        except InvalidPipeMgrOperation as e:
          pass

        info = mirror_session(MirrorType_e.PD_MIRROR_TYPE_NORM,
                              Direction_e.PD_DIR_EGRESS,
                              sids[1],
                              swports[1],
                              True)
        self.mirror.mirror_session_create(shdl, dt, info)
        self.conn_mgr.complete_operations(shdl)
        next_s_info = self.mirror.mirror_session_get_next(shdl, dev_id, first_s_info.mir_id)
        self.assertTrue(next_s_info.mir_id == sids[1], "Get next Mirror Session Info error: sid %d, expected sid %d" % (next_s_info.mir_id, sids[1]))
        try:
          next_s_info = self.mirror.mirror_session_get_next(shdl, dev_id, sids[2])
          self.assertTrue(False)
        except InvalidPipeMgrOperation as e:
          pass

        self.mirror.mirror_session_delete(shdl, dt, sids_coal[0])
        self.mirror.mirror_session_delete(shdl, dt, sids[1])

    finally:
      entry_count = self.client.p0_get_entry_count(shdl, dt)
      for _ in range(entry_count):
        hdl = self.client.p0_get_first_entry_handle(shdl, dt)
        self.client.p0_table_delete(shdl, dev_id, hdl)
      self.conn_mgr.client_cleanup(shdl)
